package com.scau.clothes.sale.service.impl;

import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONObject;
import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayClient;
import com.alipay.api.DefaultAlipayClient;
import com.alipay.api.internal.util.AlipaySignature;
import com.alipay.api.request.AlipayTradePagePayRequest;
import com.alipay.api.request.AlipayTradeRefundRequest;
import com.alipay.api.response.AlipayTradeRefundResponse;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.scau.clothes.core.core.entity.AjaxResult;
import com.scau.clothes.core.util.CheckObjectIsNullUtils;
import com.scau.clothes.sale.config.AlipayConfig;
import com.scau.clothes.sale.config.DiscountConfig;
import com.scau.clothes.sale.constants.PaymentStatus;
import com.scau.clothes.sale.constants.PaymentType;
import com.scau.clothes.sale.constants.SaleClothesStatus;
import com.scau.clothes.sale.constants.SaleStatus;
import com.scau.clothes.sale.mapper.PaymentMapper;
import com.scau.clothes.sale.mapper.ReturnBackMapper;
import com.scau.clothes.sale.mapper.SaleClothesMapper;
import com.scau.clothes.sale.mapper.SaleMapper;
import com.scau.clothes.sale.model.dto.ExchangeDto;
import com.scau.clothes.sale.model.dto.ReturnDto;
import com.scau.clothes.sale.model.entity.Payment;
import com.scau.clothes.sale.model.entity.ReturnBack;
import com.scau.clothes.sale.model.entity.Sale;
import com.scau.clothes.sale.model.entity.SaleClothes;
import com.scau.clothes.sale.model.vo.PieDataVo;
import com.scau.clothes.sale.service.SaleService;
import com.scau.clothes.stock.mapper.ClothesMapper;
import com.scau.clothes.stock.mapper.ClothesNormMapper;
import com.scau.clothes.stock.model.dto.ClothesDto;
import com.scau.clothes.stock.model.entity.Clothes;
import com.scau.clothes.stock.model.entity.ClothesNorm;
import com.scau.clothes.stock.service.ClothesNormService;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.apache.commons.lang.RandomStringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

@Service
public class SaleServiceImpl extends ServiceImpl<SaleMapper, Sale> implements SaleService {

    @Autowired
    private SaleMapper saleMapper;
    @Autowired
    private PaymentMapper paymentMapper;
    @Autowired
    private ReturnBackMapper returnBackMapper;

    @Autowired
    private ClothesNormService clothesNormService;
    @Autowired
    private ClothesNormMapper clothesNormMapper;

    @Autowired
    private SaleClothesMapper saleClothesMapper;

    @Resource
    private AlipayConfig alipayConfig;
    @Resource
    private DiscountConfig discountConfig;

    private Sale currentSale;



    @Override
    public Sale makeNewSale() {
        currentSale = new Sale();
        Date saleDate = new Date();
        currentSale.setSaleTime(saleDate);
        String saleNo = "so-"+ IdUtil.getSnowflakeNextId();
        currentSale.setSaleNo(saleNo);
        currentSale.setStatus(SaleStatus.ORDER_RESERVED);
        currentSale.setTotal(new BigDecimal("0.00"));
        return currentSale;
    }

    @Override
    public void makeLineItem(ClothesNorm clothesNorm, int quantity) {
        if(!isEntered(clothesNorm.getClothesNormId(),quantity)){
            SaleClothes saleClothes = new SaleClothes();
            saleClothes.setClothesNormId(clothesNorm.getClothesNormId());
            saleClothes.setClothesNorm(clothesNorm);
            saleClothes.setPrice(clothesNorm.getPrice());
            saleClothes.setQuantity(quantity);
            currentSale.getSaleClothes().add(saleClothes);
        }
    }

    @Override
    public BigDecimal endSale() {
        BigDecimal total = this.getTotal();
        currentSale.setTotal(total);
        currentSale.setStatus(SaleStatus.ORDER_RESERVED);
        currentSale.setCreateTime(new Date());
//        System.out.println(currentSale);
        saleMapper.insert(currentSale);
        // 将订单中商品持久化
        insertSaleItemsOfCurrentSale(currentSale);
        return total;
    }

    @Override
    public BigDecimal makePaymentByCash(Sale sale, BigDecimal cashTendered) {
        Sale getSale = saleMapper.selectById(sale.getSaleId());
        BigDecimal total = getSale.getTotal();
        Payment payment = new Payment();
        payment.setAmount(total);
        payment.setPayType(PaymentType.CASH_PAY);
        payment.setPayTime(new Date());
        payment.setPayStatus(PaymentStatus.PAYMENT_PAID);
        paymentMapper.insert(payment);
        getSale.setPaymentId(payment.getPaymentId());
        getSale.setPayment(payment);
        getSale.setStatus(SaleStatus.ORDER_PAID);
        getSale.setUpdateTime(new Date());
        // change为找零
        BigDecimal change = cashTendered.subtract(total);
        saleMapper.updateById(getSale);
        // 更新订单商品明细的状态和更新时间
        updateSaleItemsOfCurrentSaleForMakePayment(getSale);
        return change;
    }

    @Override
    public void makePaymentByAlipay(Long saleId, HttpServletResponse httpResponse) throws Exception {
        Sale getSale = saleMapper.selectById(saleId);
        BigDecimal total = getSale.getTotal();
        Payment payment = new Payment();
        payment.setAmount(total);
        payment.setPayType(PaymentType.ALI_PAY);
        payment.setPayTime(new Date());
        payment.setPayStatus(PaymentStatus.PAYMENT_UNPAID);
        paymentMapper.insert(payment);
        getSale.setPayment(payment);
        getSale.setPaymentId(payment.getPaymentId());
        getSale.setUpdateTime(new Date());
        saleMapper.updateById(getSale);
        //调用支付宝接口
        // 1. 创建Client，通用SDK提供的Client，负责调用支付宝的API
        AlipayClient alipayClient = new DefaultAlipayClient(alipayConfig.getServerUrl(), alipayConfig.getAppId(),
                alipayConfig.getPrivateKey(), alipayConfig.getFormat(), alipayConfig.getCharset(), alipayConfig.getAlipayPublicKey(), alipayConfig.getSignType());

        // 2. 创建 Request并设置Request参数
        AlipayTradePagePayRequest request = new AlipayTradePagePayRequest();  // 发送请求的 Request类
        request.setNotifyUrl(alipayConfig.getNotifyUrl());
        JSONObject bizContent = new JSONObject();
        bizContent.set("out_trade_no", +payment.getPaymentId());  // 我们自己生成的订单编号
        bizContent.set("total_amount", payment.getAmount()); // 订单的总金额
        bizContent.set("subject", "订单支付");   // 支付的名称
        bizContent.set("product_code", "FAST_INSTANT_TRADE_PAY");  // 固定配置
        request.setBizContent(bizContent.toString());

        // 执行请求，拿到响应的结果，返回给浏览器
        String form = "";
        try {
            form = alipayClient.pageExecute(request).getBody(); // 调用SDK生成表单
        } catch (AlipayApiException e) {
            e.printStackTrace();
        }
        httpResponse.setContentType("text/html;charset=" + alipayConfig.getCharset());
        httpResponse.getWriter().write(form);// 直接将完整的表单html输出到页面
        httpResponse.getWriter().flush();
        httpResponse.getWriter().close();
    }

    @Override
    public String payNotify(HttpServletRequest request) throws Exception {
        if (request.getParameter("trade_status").equals("TRADE_SUCCESS")) {
            System.out.println("=========支付宝异步回调========");
            Map<String, String> params = new HashMap<>();
            Map<String, String[]> requestParams = request.getParameterMap();
            for (String name : requestParams.keySet()) {
                params.put(name, request.getParameter(name));
                // System.out.println(name + " = " + request.getParameter(name));
            }

            String tradeNo = params.get("out_trade_no");
            String gmtPayment = params.get("gmt_payment");
            String alipayTradeNo = params.get("trade_no");

            String sign = params.get("sign");
            String content = AlipaySignature.getSignCheckContentV1(params);
            boolean checkSignature = AlipaySignature.rsa256CheckContent(content, sign, alipayConfig.getAlipayPublicKey(), "UTF-8"); // 验证签名
            // 支付宝验签
            if (checkSignature) {
                // 验签通过
                System.out.println("交易名称: " + params.get("subject"));
                System.out.println("交易状态: " + params.get("trade_status"));
                System.out.println("支付宝交易凭证号: " + params.get("trade_no"));
                System.out.println("商户订单号: " + params.get("out_trade_no"));
                System.out.println("交易金额: " + params.get("total_amount"));
                System.out.println("买家在支付宝唯一id: " + params.get("buyer_id"));
                System.out.println("买家付款时间: " + params.get("gmt_payment"));
                System.out.println("买家付款金额: " + params.get("buyer_pay_amount"));

                // 更新订单未已支付
                Payment payment = paymentMapper.selectById(Long.valueOf(tradeNo));
                payment.setPayTime(DateUtil.parse(gmtPayment, "yyyy-MM-dd HH:mm:ss"));
                payment.setAlipayTraceNo(alipayTradeNo);
                payment.setPayStatus(PaymentStatus.PAYMENT_PAID);
                paymentMapper.updateById(payment);
                QueryWrapper<Sale> saleQueryWrapper = new QueryWrapper<>();
                saleQueryWrapper.eq("payment_id", payment.getPaymentId());
                saleQueryWrapper.ne("is_delete", 1);
                Sale sale = saleMapper.selectOne(saleQueryWrapper);
                sale.setStatus(SaleStatus.ORDER_PAID);
                sale.setUpdateTime(new Date());
                saleMapper.updateById(sale);
                this.updateSaleItemsOfCurrentSaleForMakePayment(sale);
            }
        }
        return "success";
    }

    @Override
    public Boolean returnBack(ReturnDto returnDto) throws Exception {
        Sale sale = saleMapper.selectById(returnDto.getSaleId());
        QueryWrapper<SaleClothes> saleClothesQueryWrapper = new QueryWrapper<>();
        saleClothesQueryWrapper.ne("is_delete", 1);
        saleClothesQueryWrapper.eq("sale_id", returnDto.getSaleId());
        List<SaleClothes> list = saleClothesMapper.selectList(saleClothesQueryWrapper);
        sale.setSaleClothes(list);
        Payment payment = paymentMapper.selectById(sale.getPaymentId());
        CheckObjectIsNullUtils checkObjectIsNullUtils = new CheckObjectIsNullUtils();
        if (checkObjectIsNullUtils.objCheckIsNull(returnDto))
            return false;
        if (payment.getPayType().equals("支付宝支付")) {
            // 1. 创建Client，通用SDK提供的Client，负责调用支付宝的API
            AlipayClient alipayClient = new DefaultAlipayClient(alipayConfig.getServerUrl(), alipayConfig.getAppId(),
                    alipayConfig.getPrivateKey(), alipayConfig.getFormat(), alipayConfig.getCharset(), alipayConfig.getAlipayPublicKey(), alipayConfig.getSignType());
            // 2. 创建 Request，设置参数
            AlipayTradeRefundRequest request = new AlipayTradeRefundRequest();
            JSONObject bizContent = new JSONObject();
            bizContent.set("trade_no", payment.getAlipayTraceNo());  // 支付宝回调的订单流水号
            if (returnDto.getSaleClothes()!=null){
                BigDecimal amount = null;
                for (SaleClothes s : returnDto.getSaleClothes()) {
                    amount.add(s.getPrice().multiply(BigDecimal.valueOf(s.getQuantity())));
                }
                bizContent.set("refund_amount", amount);  // 订单的总金额
                bizContent.set("out_request_no", RandomStringUtils.randomAlphanumeric(13));   //部分退款
            }
            bizContent.set("refund_amount", sale.getTotal());  // 订单的总金额

            request.setBizContent(bizContent.toString());

            // 3. 执行请求
            AlipayTradeRefundResponse response = alipayClient.execute(request);
            if (response.isSuccess()) {  // 退款成功，isSuccess 为true
                System.out.println("调用成功");

                // 4. 更新数据库状态
                //更新状态
                if (returnDto.getSaleClothes()!=null){
                    sale.setStatus(SaleStatus.ORDER_PART_RETURN);
                    payment.setPayStatus(PaymentStatus.PAYMENT_RETURN);
                    for (SaleClothes sc : returnDto.getSaleClothes()) {
                        sc.setStatus(SaleClothesStatus.ITEM_RETURN);
                        sc.setUpdateTime(new Date());
                        saleClothesMapper.updateById(sc);
                        //更新库存
                        ClothesNorm cn = clothesNormService.getById(sc.getClothesNormId());
                        cn.setQuantity(cn.getQuantity() + sc.getQuantity());
                        cn.setSales(cn.getSales() - sc.getQuantity());
                        clothesNormService.updateClothesNorm(cn);
                        //插入退货表
                        ReturnBack returnBack = new ReturnBack();
                        BeanUtils.copyProperties(returnBack, sc);
                        returnBackMapper.insert(returnBack);
                    }
                    saleMapper.updateById(sale);
                    paymentMapper.updateById(payment);
                }
                else {
                    sale.setStatus(SaleStatus.ORDER_RETURN);
                    payment.setPayStatus(PaymentStatus.PAYMENT_RETURN);
                    for (SaleClothes sc : sale.getSaleClothes()) {
                        sc.setStatus(SaleClothesStatus.ITEM_RETURN);
                        sc.setUpdateTime(new Date());
                        saleClothesMapper.updateById(sc);
                        //更新库存
                        ClothesNorm cn = clothesNormService.getById(sc.getClothesNormId());
                        cn.setQuantity(cn.getQuantity() + sc.getQuantity());
                        cn.setSales(cn.getSales() - sc.getQuantity());
                        clothesNormService.updateClothesNorm(cn);
                    }
                    saleMapper.updateById(sale);
                    paymentMapper.updateById(payment);
                    //插入退货表
                    ReturnBack returnBack = new ReturnBack();
                    returnBack.setSaleId(sale.getSaleId());
                    returnBack.setPrice(sale.getTotal());
                    returnBackMapper.insert(returnBack);
                }

            }
            return response.isSuccess();
        }

        if (returnDto.getSaleClothes()!=null){
            sale.setStatus(SaleStatus.ORDER_PART_RETURN);
            payment.setPayStatus(PaymentStatus.PAYMENT_RETURN);
            for (SaleClothes sc : returnDto.getSaleClothes()) {
                sc.setStatus(SaleClothesStatus.ITEM_RETURN);
                sc.setUpdateTime(new Date());
                saleClothesMapper.updateById(sc);
                //更新库存
                ClothesNorm cn = clothesNormService.getById(sc.getClothesNormId());
                cn.setQuantity(cn.getQuantity() + sc.getQuantity());
                cn.setSales(cn.getSales() - sc.getQuantity());
                clothesNormService.updateClothesNorm(cn);
                //插入退货表
                ReturnBack returnBack = new ReturnBack();
                BeanUtils.copyProperties(returnBack, sc);
                returnBackMapper.insert(returnBack);
            }
            saleMapper.updateById(sale);
            paymentMapper.updateById(payment);
        }
        else {
            sale.setStatus(SaleStatus.ORDER_RETURN);
            payment.setPayStatus(PaymentStatus.PAYMENT_RETURN);
            for (SaleClothes sc : sale.getSaleClothes()) {
                sc.setStatus(SaleClothesStatus.ITEM_RETURN);
                sc.setUpdateTime(new Date());
                saleClothesMapper.updateById(sc);
                //更新库存
                ClothesNorm cn = clothesNormService.getById(sc.getClothesNormId());
                cn.setQuantity(cn.getQuantity() + sc.getQuantity());
                cn.setSales(cn.getSales() - sc.getQuantity());
                clothesNormService.updateClothesNorm(cn);
            }
            saleMapper.updateById(sale);
            paymentMapper.updateById(payment);
            //插入退货表
            ReturnBack returnBack = new ReturnBack();
            returnBack.setSaleId(sale.getSaleId());
            returnBack.setPrice(sale.getTotal());
            returnBackMapper.insert(returnBack);
        }
        return true;
    }

    @Override
    public Boolean exchange(ExchangeDto exchangeDto) {
        CheckObjectIsNullUtils checkObjectIsNullUtils = new CheckObjectIsNullUtils();
        if (!checkObjectIsNullUtils.objCheckIsNull(exchangeDto)){
            SaleClothes saleClothes = saleClothesMapper.selectById(exchangeDto.getSaleClothesId());
            ClothesNorm clothesNorm = clothesNormMapper.getOneByClothesNormId(saleClothes.getClothesNormId());
            ClothesNorm exchange = clothesNormMapper.selectById(exchangeDto.getClothesNormId());
            //更新原库存
            clothesNorm.setQuantity(clothesNorm.getQuantity() + saleClothes.getQuantity());
            clothesNorm.setSales(clothesNorm.getSales() - saleClothes.getQuantity());
            clothesNormService.updateClothesNorm(clothesNorm);
            //更新交换库存
            exchange.setQuantity(exchange.getQuantity() - saleClothes.getQuantity());
            exchange.setSales(exchange.getSales() + saleClothes.getQuantity());
            clothesNormService.updateClothesNorm(exchange);
            //更新订单明细
            saleClothes.setClothesNormId(exchange.getClothesNormId());
            saleClothesMapper.updateById(saleClothes);
            return true;
        }
        return false;
    }


    @Override
    public Sale cancelSale(Sale sale) {
        Sale getSale = saleMapper.selectById(sale.getSaleId());
        getSale.setStatus(SaleStatus.ORDER_CANCELED);
        getSale.setUpdateTime(new Date());
        saleMapper.updateById(getSale);
        this.updateSaleItemsOfCurrentSaleForCancelPayment(getSale);
        return getSale;
    }

    @Override
    public List<Sale> listAllSale(Sale sale) {
        List<Sale> saleList = saleMapper.listAllSale(sale);
        for (Sale s : saleList) {
            List<SaleClothes> saleClothesList = s.getSaleClothes();
            for (SaleClothes sc : saleClothesList) {
                sc.setClothesNorm(clothesNormMapper.getOneByClothesNormId(sc.getClothesNormId()));
            }
        }
        return saleList;
    }

    @Override
    public BigDecimal getClothesNormDiscountPrice(BigDecimal price) {
        price = price.multiply(discountConfig.getSeasonDiscount());
        return price;
    }


    /**
     * 计算总金额
     * @return
     */
    private BigDecimal getTotal(){
        BigDecimal total = new BigDecimal(0);
        List<SaleClothes> saleClothesList = currentSale.getSaleClothes();
        for(SaleClothes sc : saleClothesList) {
            total = total.add(getSubTotal(sc));
        }
        return total;
    }

    /**
     * 判断商品是否已录入
     * @param clothesNormId
     * @param quantity
     * @return
     */
    private boolean isEntered(Long clothesNormId,int quantity){
        boolean flag = false;
        for(SaleClothes si : currentSale.getSaleClothes()){
            // 如果已经录入则修改数量
            if(clothesNormId.equals(si.getClothesNorm().getClothesNormId())) {
                flag = true;
                int quantityOriginal = si.getQuantity();
                si.setQuantity(quantityOriginal + quantity);
            }
        }
        return flag;
    }

    /**
     * 计算小计
     * @return 小计
     */
    public BigDecimal getSubTotal(SaleClothes saleClothes){
        BigDecimal subTotal =  saleClothes.getPrice().multiply(new BigDecimal(saleClothes.getQuantity()));
        return subTotal;
    }

    /**
     * 将Sale中的saleItem存入数据库
     * @param currentSale 当前sale对象
     */
    private void insertSaleItemsOfCurrentSale(Sale currentSale) {
        List<SaleClothes> saleClothesList = currentSale.getSaleClothes();
        // 视频中新增list代码冗余，删除
        for(SaleClothes sc : saleClothesList) {
            sc.setStatus(SaleClothesStatus.ITEM_RESERVED);
            sc.setIsDelete(0l);
            sc.setCreateTime(new Date());
            // 注意sale插入db自增生成id后才能getSaleId()取到id值
            sc.setSaleId(currentSale.getSaleId());
        }
        saleMapper.batchInsertSaleItemOfCurrentSale(saleClothesList);
    }

    private void updateSaleItemsOfCurrentSaleForMakePayment(Sale currentSale){
        SaleClothes param = new SaleClothes();
        param.setSaleId(currentSale.getSaleId());
        QueryWrapper<SaleClothes> queryWrapper = new QueryWrapper<>();
        if (param.getSaleId() != null)
            queryWrapper.eq("sale_id", param.getSaleId());
        List<SaleClothes> saleClothesList = saleClothesMapper.selectList(queryWrapper);
        for (SaleClothes sc : saleClothesList) {
            sc.setStatus(SaleClothesStatus.ITEM_PAID);
            sc.setUpdateTime(new Date());
            saleClothesMapper.updateById(sc);
            //更新库存
            ClothesNorm cn = clothesNormService.getById(sc.getClothesNormId());
            cn.setQuantity(cn.getQuantity() - sc.getQuantity());
            cn.setSales(cn.getSales() + sc.getQuantity());
            clothesNormService.updateClothesNorm(cn);
        }

    }

    private void updateSaleItemsOfCurrentSaleForCancelPayment(Sale currentSale){
        SaleClothes param = new SaleClothes();
        param.setSaleId(currentSale.getSaleId());
        QueryWrapper<SaleClothes> queryWrapper = new QueryWrapper<>();
        if (StrUtil.isNotBlank(param.getSaleId().toString()))
            queryWrapper.eq("sale_id", param.getSaleId());
        List<SaleClothes> saleClothesList = saleClothesMapper.selectList(queryWrapper);
        for (SaleClothes sc : saleClothesList) {
            sc.setStatus(SaleClothesStatus.ITEM_CANCELED);
            sc.setUpdateTime(new Date());
            saleClothesMapper.updateById(sc);
        }
    }

}

